package top.hypnos.bigdata.filesystem;

import top.hypnos.bigdata.filesystem.command.CommandException;
import top.hypnos.bigdata.filesystem.record.ObjectInputStreamImpl;
import top.hypnos.bigdata.filesystem.record.ObjectOutputStreamImpl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileSnapshot implements Snapshot {

    private final File basePath;
    private final BinLog binLog;

    public FileSnapshot(final File basePath, final BinLog binLog) throws IOException {
        this.basePath = basePath;
        this.binLog = binLog;

        if (!basePath.exists()) {
            if (!basePath.mkdirs()) {
                throw new IOException("无法创建目录：" + basePath.getAbsolutePath());
            }
        }
    }

    @Override
    public void save(final MemDataTree memDataTree) throws IOException {
        final var snapshotFile = new File(basePath, "snapshot");
        try (final var fileOutput = new FileOutputStream(snapshotFile)) {
            final var out = new ObjectOutputStreamImpl(fileOutput);
            // TODO: 存在并发问题，保存快照时有数据写入，事务ID会不一致
            memDataTree.serialize(out);
        }
    }

    @Override
    public MemDataTree recover() throws IOException, CommandException {
        // 恢复快照中的数据
        final var result = new MemDataTree(binLog);
        final var snapshotFile = new File(basePath, "snapshot");
        if (snapshotFile.exists()) {
            try (final var fileInput = new FileInputStream(snapshotFile)) {
                final var in = new ObjectInputStreamImpl(fileInput);
                result.deserialize(in);
            }
        }

        // 追加快照之后的binlog中的命令
        final var commands = binLog.loadAfter(result.getTxnId());
        for (final var command : commands) {
            command.preExecute(result);
            command.commit();
        }

        return result;
    }
}
